#include "map.h"

#include <qfile.h>
#include <qdatastream.h>
#include "exceptions.h"

#include <qdebug.h>

Map::Map(const QString& filePath, Tileset& tileset, bool forceCreation, const QString& name, quint8 width, quint8 height)
    : m_name(name), m_file(filePath), m_tileset(tileset), m_width(width), m_height(height)
{
    if(m_file.exists() || forceCreation)    //If the file exists or can be created
    {
        //Creates the matrix
        m_matrix = new MapSquare*[m_height];
        for(quint8 j = 0 ; j < m_height ; j++)
        {
            m_matrix[j] = new MapSquare[m_width];
        }

        //If the map file doesn't exist, fills the matrix with grass tiles and saves it
        if(!m_file.exists())
        {
            fillTiles(0);
            resetCharacters();
            resetEvents();
            save();
        }

        //The file now exists
        //Reads the matrix entries from the file
        read();
    }
    else
    {
        throw EX_FILE_NOT_FOUND(filePath);
    }
}

Map::~Map()
{
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        delete[] m_matrix[j];
    }
    delete[] m_matrix;

}

void Map::read()
{
    m_file.open(QIODevice::ReadOnly);
    QDataStream inData(&m_file);

    //Reads the map name
    char* name;
    inData >> name;
    m_name = name;
    delete[] name;

    //Reads the matrix dimensions
    inData >> m_width;
    inData >> m_height;

    //Then reads the matrix entries
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            inData >> m_matrix[j][i].tile;
            inData >> m_matrix[j][i].character;
            inData >> m_matrix[j][i].event;
        }
    }

    m_file.close();
}

void Map::save()
{
    m_file.open(QIODevice::WriteOnly | QIODevice::Truncate);
    QDataStream outData(&m_file);

    //Writes the map name
    std::string name = m_name.toStdString();
    outData << name.c_str();

    //Writes the matrix dimensions
    outData << m_width;
    outData << m_height;

    //Then writes the matrix entries
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            outData << m_matrix[j][i].tile;
            outData << m_matrix[j][i].character;
            outData << m_matrix[j][i].event;
        }
    }

    m_file.close();
}

const QString& Map::getName() const
{
    return m_name;
}

quint8 Map::getWidth() const
{
    return m_width;
}

quint8 Map::getHeight() const
{
    return m_height;
}

//TILES

void Map::modifyTile(quint8 x, quint8 y, quint16 value)
{
    //Sets the (y,x) entry of the matrix to value
    if(x >= m_width)
        throw EX_OUT_OF_RANGE(m_width, x);
    else if(y >= m_height)
        throw EX_OUT_OF_RANGE(m_height, y);
    else
        m_matrix[y][x].tile = value;
}

void Map::fillTiles(quint16 value)
{
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            m_matrix[j][i].tile = value;
        }
    }
}

const Tile& Map::getTile(quint8 x, quint8 y) const
{
    return m_tileset.getTile(m_matrix[y][x].tile);
}

//CHARACTERS
void Map::resetCharacters()
{
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            m_matrix[j][i].character = UNIT_NONE;
        }
    }
}

void Map::putCharacter(quint8 x, quint8 y, quint16 value)
{
    //Sets the (y,x) entry of the matrix to value
    if(x >= m_width)
        throw EX_OUT_OF_RANGE(m_width, x);
    else if(y >= m_height)
        throw EX_OUT_OF_RANGE(m_height, y);
    else
        m_matrix[y][x].character = value;
}

quint8 Map::getCharacterNumber(quint8 x, quint8 y) const
{
    return m_matrix[y][x].character;
}

void Map::removeCharacter(quint8 x, quint8 y)
{
    m_matrix[y][x].character = UNIT_NONE;
}

void Map::putSpawnPoint(quint8 x, quint8 y)
{
    m_matrix[y][x].character = UNIT_SPAWN;
}

bool Map::containsCharacter(quint8 x, quint8 y) const
{
    return (m_matrix[y][x].character != UNIT_NONE
            && m_matrix[y][x].character != UNIT_SPAWN);
}

bool Map::containsSpawnPoint(quint8 x, quint8 y) const
{
    return m_matrix[y][x].character == UNIT_SPAWN;
}

bool Map::containsNoUnit(quint8 x, quint8 y) const
{
    return m_matrix[y][x].character == UNIT_NONE;
}

quint16 Map::getTileNumber(quint8 x, quint8 y) const
{
    return m_matrix[x][y].tile;
}
// pour enreg dans le fichier pour une gamemap

//EVENTS
void Map::resetEvents()
{
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            m_matrix[j][i].event = 0;
        }
    }
}
